tuple是有序的collection type,但其內可以含有不同的型別。例如:
select ('Apple', 7);
{('Apple', 7)}
請注意,此query返回結果依然是EdgeDBSet。
相對於array可以使用[]來索引,tuple則可以使用.來取得元素,且可以分為unnamed及named兩種。
unnamed tuple利用.搭配數字(一樣由0開始)來取得元素。例如:
select ('Apple', 7).0;
{'Apple'}
named tuple除了可以如unnamed tuple一樣,利用.搭配數字來取得元素。例如:
select (fruit:='Apple', n:=7).0;
{'Apple'}
也可以利用.搭配str來取得元素。例如:
select (fruit:='Apple', n:=7).fruit;
{'Apple'}
tuple一般用於整理外部所收集的資訊,來供後續的bulk operation使用。以下舉了兩種使用情況,希望能幫助大家熟悉tuple的使用。
考慮schema如下:
type Todo {
    priority: int64;
    note: str
}
假設此時我們想insert兩個Todo object,除了一個一個insert外,可以巧妙運用tuple進行bulk inserts:
with todos:= {(10, "check emails"), 
              (3, "code review")}
for todo in todos 
union (
    insert Todo {
        priority:= todo.0,
        note:= todo.1
    }
);
又或者:
with todos:= {(priority:=10, note:="check emails"), 
              (priority:=3, note:="code review")}
for todo in todos 
union (
    insert Todo {
        priority:= todo.priority,
        note:= todo.note
    }
);
以上可以看出,我們在with區塊內使用tuple來幫忙整理資訊,供後續bulk inserts時使用,這是個非常典型的用法。
考慮schema如下:
type Remark {
    required location: str {
        constraint exclusive
    }
    multi todos: tuple<priority:int64, note: str>
}
可以觀察到tuple直接定義在multi todos property中。
假設此時我們想insert兩個Remark object,可以這麼寫:
with works:= {("office", (10, "check emails"), (3, "code review")), 
              ("home", (7, "do the laundry"), (9, "clean the bathroom"))}
for work in works 
union (
    insert Remark {
        location:= work.0,
        todos:= work.1 union work.2
    }
);
又或者:
with works:= {(location:="office",
               todo1:= (10, "check emails"),
               todo2:= (3, "code review")), 
              (location:="home",
               todo1:= (7, "do the laundry"),
               todo2:= (9, "clean the bathroom"))}
for work in works 
union (
    insert Remark {
        location:= work.location,
        todos:= work.todo1 union work.todo2
    }
);
以上可以看出,將tuple使用於schema內,可有效收集不同型別在同一個property中。
range()像是一個產生器,可以用來產生整數、浮點數及與時間相關的scalar type。
range與range間可以進行運算,例如:
select range(1, 10) + range(5, 15);
{range(1, 15)}
一般常用的情況為使用range_unpack()來得到一連串的數字或時間。例如:
select range_unpack(range(1, 10));
{1, 2, 3, 4, 5, 6, 7, 8, 9}
或是:
select range_unpack(range(
  <cal::local_date>'2024-08-01',
  <cal::local_date>'2024-08-05'));
{
  <cal::local_date>'2024-08-01',
  <cal::local_date>'2024-08-02',
  <cal::local_date>'2024-08-03',
  <cal::local_date>'2024-08-04',
}
以上可以觀察出,range()預設是包含下界但不包含上界的。但我們可以使用inc_lower及inc_upper兩個參數來改變這個設定。例如:
select range_unpack(range(1, 10, inc_lower:=false, inc_upper:=true));
{2, 3, 4, 5, 6, 7, 8, 9, 10}
與預設結果相比,我們少了1卻多了10。
multirange()可以將多個range()區段視為一個整體,例如:
select multirange([range(8, 10), range(1, 4), range(2, 5)]);
{[range(1, 5), range(8, 10)]}
請留意這裡range(1, 4)與range(2, 5)合併為range(1, 5)。如果我們使用multirange_unpack()與range_unpack(),可以觀察到裡面所含的元素:
with mr_unpack:= multirange_unpack(
                     multirange(
                         [range(8, 10), range(1, 4), range(2, 5)]
                     )
                 )
select range_unpack(mr_unpack);
{1, 2, 3, 4, 8, 9}
一般常用的情況為使用contains()來判斷某個值、某個range甚至某個multirange是不是在一個multirange內。
select contains(
  multirange([range(1, 4), range(7)]),
  multirange([range(1, 2), range(8, 10)]),
);
{true}
其結果為true。這是因為multirange([range(1, 4), range(7)])可以涵蓋multirange([range(1, 2), range(8, 10)])。其中range(1, 4)涵蓋了range(1, 2)且range(7)(下界為7,上界unbounded)涵蓋了range(8, 10)。
或是:
select contains(
  multirange([range(
                  <cal::local_date>'2024-01-01',
                  <cal::local_date>'2024-07-01'),
              range(
                  <cal::local_date>'2024-08-01',
                  <cal::local_date>'2025-01-01')]),
  <cal::local_date>'2024-07-01'
);
{false}
其結果為false,因為range()預設是包含下界但不包含上界,所以2024-07-01並不在範圍內。